1/*
2 * Copyright (C) 2007 Ryan Leavengood <leavengood@gmail.com>
3 * Copyright (C) 2010 Stephan A��mus <superstippi@gmx.de>
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *	notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *	notice, this list of conditions and the following disclaimer in the
14 *	documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "BrowserApp.h"
30
31#include <AboutWindow.h>
32#include <Alert.h>
33#include <Autolock.h>
34#include <Catalog.h>
35#include <Directory.h>
36#include <Entry.h>
37#include <FindDirectory.h>
38#include <Locale.h>
39#include <Path.h>
40#include <Screen.h>
41#include <debugger.h>
42
43#include <stdio.h>
44
45#include "BrowserWindow.h"
46#include "BrowsingHistory.h"
47#include "DownloadWindow.h"
48#include "SettingsMessage.h"
49#include "SettingsWindow.h"
50#include "svn_revision.h"
51#include "NetworkCookieJar.h"
52#include "WebKitInfo.h"
53#include "WebPage.h"
54#include "WebSettings.h"
55#include "WebView.h"
56
57
58#undef B_TRANSLATION_CONTEXT
59#define B_TRANSLATION_CONTEXT "WebPositive"
60
61const char* kApplicationSignature = "application/x-vnd.Haiku-WebPositive";
62const char* kApplicationName = B_TRANSLATE_SYSTEM_NAME("WebPositive");
63static const uint32 PRELOAD_BROWSING_HISTORY = 'plbh';
64
65#define ENABLE_NATIVE_COOKIES 0
66
67
68BrowserApp::BrowserApp()
69	:
70	BApplication(kApplicationSignature),
71	fWindowCount(0),
72	fLastWindowFrame(50, 50, 950, 750),
73	fLaunchRefsMessage(0),
74	fInitialized(false),
75	fSettings(NULL),
76	fCookies(NULL),
77	fCookieJar(NULL),
78	fDownloadWindow(NULL),
79	fSettingsWindow(NULL),
80	fAboutWindow(NULL)
81{
82}
83
84
85BrowserApp::~BrowserApp()
86{
87	delete fLaunchRefsMessage;
88	delete fSettings;
89	delete fCookies;
90	delete fCookieJar;
91
92	if (fAboutWindow != NULL && fAboutWindow->Lock())
93		fAboutWindow->Quit();
94}
95
96
97void
98BrowserApp::AboutRequested()
99{
100	if (fAboutWindow == NULL) {
101		// create the about window
102
103		const char* authors[] = {
104			"Andrea Anzani",
105			"Stephan A��mus",
106			"Alexandre Deckner",
107			"Rene Gollent",
108			"Ryan Leavengood",
109			"Michael Lotz",
110			"Maxime Simon",
111			NULL
112		};
113
114		BString aboutText("");
115		aboutText << "HaikuWebKit " << WebKitInfo::HaikuWebKitVersion();
116		aboutText << " (" << WebKitInfo::HaikuWebKitRevision() << ")";
117		aboutText << "\nWebKit " << WebKitInfo::WebKitVersion();
118		aboutText << " (" << WebKitInfo::WebKitRevision() << ")";
119
120		fAboutWindow = new BAboutWindow(kApplicationName,
121			kApplicationSignature);
122		fAboutWindow->AddCopyright(2007, "Haiku, Inc.");
123		fAboutWindow->AddAuthors(authors);
124		fAboutWindow->AddExtraInfo(aboutText.String());
125		fAboutWindow->Show();
126	} else if (fAboutWindow->IsHidden())
127		fAboutWindow->Show();
128	else
129		fAboutWindow->Activate();
130}
131
132
133void
134BrowserApp::ArgvReceived(int32 argc, char** argv)
135{
136	BMessage message(B_REFS_RECEIVED);
137	for (int i = 1; i < argc; i++) {
138		if (strcmp("-f", argv[i]) == 0
139			|| strcmp("--fullscreen", argv[i]) == 0) {
140			message.AddBool("fullscreen", true);
141			continue;
142		}
143		const char* url = argv[i];
144		BEntry entry(argv[i], true);
145		BPath path;
146		if (entry.Exists() && entry.GetPath(&path) == B_OK)
147			url = path.Path();
148		message.AddString("url", url);
149	}
150	// Upon program launch, it will buffer a copy of the message, since
151	// ArgReceived() is called before ReadyToRun().
152	RefsReceived(&message);
153}
154
155
156void
157BrowserApp::ReadyToRun()
158{
159	// Since we will essentially run the GUI...
160	set_thread_priority(Thread(), B_DISPLAY_PRIORITY);
161
162	BWebPage::InitializeOnce();
163	BWebPage::SetCacheModel(B_WEBKIT_CACHE_MODEL_WEB_BROWSER);
164
165	BPath path;
166	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK
167		&& path.Append(kApplicationName) == B_OK
168		&& create_directory(path.Path(), 0777) == B_OK) {
169
170		BWebSettings::SetPersistentStoragePath(path.Path());
171	}
172
173	BString mainSettingsPath(kApplicationName);
174	mainSettingsPath << "/Application";
175	fSettings = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
176		mainSettingsPath.String());
177#if ENABLE_NATIVE_COOKIES
178	mainSettingsPath = kApplicationName;
179	mainSettingsPath << "/Cookies";
180	fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
181		mainSettingsPath.String());
182	BMessage cookieArchive;
183	cookieArchive = fCookies->GetValue("cookies", cookieArchive);
184	fCookieJar = new BNetworkCookieJar(cookieArchive);
185	BWebPage::SetCookieJar(fCookieJar);
186#endif
187
188	fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame);
189	BRect defaultDownloadWindowFrame(-10, -10, 365, 265);
190	BRect downloadWindowFrame = fSettings->GetValue("downloads window frame",
191		defaultDownloadWindowFrame);
192	BRect settingsWindowFrame = fSettings->GetValue("settings window frame",
193		BRect());
194	bool showDownloads = fSettings->GetValue("show downloads", false);
195
196	fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads,
197		fSettings);
198	if (downloadWindowFrame == defaultDownloadWindowFrame) {
199		// Initially put download window in lower right of screen.
200		BRect screenFrame = BScreen().Frame();
201		BMessage decoratorSettings;
202		fDownloadWindow->GetDecoratorSettings(&decoratorSettings);
203		float borderWidth = 0;
204		if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK)
205			borderWidth = 5;
206		fDownloadWindow->MoveTo(screenFrame.Width()
207			- fDownloadWindow->Frame().Width() - borderWidth,
208			screenFrame.Height() - fDownloadWindow->Frame().Height()
209			- borderWidth);
210	}
211	fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings);
212
213	BWebPage::SetDownloadListener(BMessenger(fDownloadWindow));
214
215	fInitialized = true;
216
217	int32 pagesCreated = 0;
218	bool fullscreen = false;
219	if (fLaunchRefsMessage) {
220		_RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen);
221		delete fLaunchRefsMessage;
222		fLaunchRefsMessage = NULL;
223	}
224	if (pagesCreated == 0)
225		_CreateNewWindow("", fullscreen);
226
227	PostMessage(PRELOAD_BROWSING_HISTORY);
228}
229
230
231void
232BrowserApp::MessageReceived(BMessage* message)
233{
234	switch (message->what) {
235	case PRELOAD_BROWSING_HISTORY:
236		// Accessing the default instance will load the history from disk.
237		BrowsingHistory::DefaultInstance();
238		break;
239	case B_SILENT_RELAUNCH:
240		_CreateNewPage("");
241		break;
242	case NEW_WINDOW: {
243		BString url;
244		if (message->FindString("url", &url) != B_OK)
245			break;
246		_CreateNewWindow(url);
247		break;
248	}
249	case NEW_TAB: {
250		BrowserWindow* window;
251		if (message->FindPointer("window",
252			reinterpret_cast<void**>(&window)) != B_OK)
253			break;
254		BString url;
255		message->FindString("url", &url);
256		bool select = false;
257		message->FindBool("select", &select);
258		_CreateNewTab(window, url, select);
259		break;
260	}
261	case WINDOW_OPENED:
262		fWindowCount++;
263		fDownloadWindow->SetMinimizeOnClose(false);
264		break;
265	case WINDOW_CLOSED:
266		fWindowCount--;
267		message->FindRect("window frame", &fLastWindowFrame);
268		if (fWindowCount <= 0)
269			PostMessage(B_QUIT_REQUESTED);
270		break;
271
272	case SHOW_DOWNLOAD_WINDOW:
273		_ShowWindow(message, fDownloadWindow);
274		break;
275	case SHOW_SETTINGS_WINDOW:
276		_ShowWindow(message, fSettingsWindow);
277		break;
278
279	default:
280		BApplication::MessageReceived(message);
281		break;
282	}
283}
284
285
286void
287BrowserApp::RefsReceived(BMessage* message)
288{
289	if (!fInitialized) {
290		delete fLaunchRefsMessage;
291		fLaunchRefsMessage = new BMessage(*message);
292		return;
293	}
294
295	_RefsReceived(message);
296}
297
298
299bool
300BrowserApp::QuitRequested()
301{
302	if (fDownloadWindow->DownloadsInProgress()) {
303		BAlert* alert = new BAlert(B_TRANSLATE("Downloads in progress"),
304			B_TRANSLATE("There are still downloads in progress, do you really "
305			"want to quit WebPositive now?"), B_TRANSLATE("Quit"),
306			B_TRANSLATE("Continue downloads"));
307		int32 choice = alert->Go();
308		if (choice == 1) {
309			if (fWindowCount == 0) {
310				if (fDownloadWindow->Lock()) {
311					fDownloadWindow->SetWorkspaces(1 << current_workspace());
312					if (fDownloadWindow->IsHidden())
313						fDownloadWindow->Show();
314					else
315						fDownloadWindow->Activate();
316					fDownloadWindow->SetMinimizeOnClose(true);
317					fDownloadWindow->Unlock();
318					return false;
319				}
320			} else
321				return false;
322		}
323	}
324
325	for (int i = 0; BWindow* window = WindowAt(i); i++) {
326		BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
327		if (!webWindow)
328			continue;
329		if (!webWindow->Lock())
330			continue;
331		if (webWindow->QuitRequested()) {
332			fLastWindowFrame = webWindow->WindowFrame();
333			webWindow->Quit();
334			i--;
335		} else {
336			webWindow->Unlock();
337			return false;
338		}
339	}
340
341	BWebPage::ShutdownOnce();
342
343	fSettings->SetValue("window frame", fLastWindowFrame);
344	if (fDownloadWindow->Lock()) {
345		fSettings->SetValue("downloads window frame", fDownloadWindow->Frame());
346		fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden());
347		fDownloadWindow->Unlock();
348	}
349	if (fSettingsWindow->Lock()) {
350		fSettings->SetValue("settings window frame", fSettingsWindow->Frame());
351		fSettingsWindow->Unlock();
352	}
353
354	BMessage cookieArchive;
355	if (fCookieJar != NULL && fCookieJar->Archive(&cookieArchive) == B_OK)
356		fCookies->SetValue("cookies", cookieArchive);
357
358	return true;
359}
360
361
362void
363BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated,
364	bool* _fullscreen)
365{
366	int32 pagesCreated = 0;
367
368	bool fullscreen;
369	if (message->FindBool("fullscreen", &fullscreen) != B_OK)
370		fullscreen = false;
371
372	entry_ref ref;
373	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
374		BEntry entry(&ref, true);
375		if (!entry.Exists())
376			continue;
377		BPath path;
378		if (entry.GetPath(&path) != B_OK)
379			continue;
380		BString url;
381		url << path.Path();
382		_CreateNewPage(url, fullscreen);
383		pagesCreated++;
384	}
385
386	BString url;
387	for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) {
388		_CreateNewPage(url, fullscreen);
389		pagesCreated++;
390	}
391
392	if (_pagesCreated != NULL)
393		*_pagesCreated = pagesCreated;
394	if (_fullscreen != NULL)
395		*_fullscreen = fullscreen;
396}
397
398
399void
400BrowserApp::_CreateNewPage(const BString& url, bool fullscreen)
401{
402	uint32 workspace = 1 << current_workspace();
403
404	bool loadedInWindowOnCurrentWorkspace = false;
405	for (int i = 0; BWindow* window = WindowAt(i); i++) {
406		BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
407		if (!webWindow)
408			continue;
409		if (webWindow->Lock()) {
410			if (webWindow->Workspaces() & workspace) {
411				if (webWindow->IsBlankTab()) {
412					if (url.Length() != 0)
413						webWindow->CurrentWebView()->LoadURL(url);
414				} else
415					webWindow->CreateNewTab(url, true);
416				webWindow->Activate();
417				webWindow->CurrentWebView()->MakeFocus(true);
418				loadedInWindowOnCurrentWorkspace = true;
419			}
420			webWindow->Unlock();
421		}
422		if (loadedInWindowOnCurrentWorkspace)
423			return;
424	}
425	_CreateNewWindow(url, fullscreen);
426}
427
428
429void
430BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen)
431{
432	// Offset the window frame unless this is the first window created in the
433	// session.
434	if (fWindowCount > 0)
435		fLastWindowFrame.OffsetBy(20, 20);
436	if (!BScreen().Frame().Contains(fLastWindowFrame))
437		fLastWindowFrame.OffsetTo(50, 50);
438
439	BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings,
440		url);
441	if (fullscreen)
442		window->ToggleFullscreen();
443	window->Show();
444}
445
446
447void
448BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url,
449	bool select)
450{
451	if (!window->Lock())
452		return;
453	window->CreateNewTab(url, select);
454	window->Unlock();
455}
456
457
458void
459BrowserApp::_ShowWindow(const BMessage* message, BWindow* window)
460{
461	BAutolock _(window);
462	uint32 workspaces;
463	if (message->FindUInt32("workspaces", &workspaces) == B_OK)
464		window->SetWorkspaces(workspaces);
465	if (window->IsHidden())
466		window->Show();
467	else
468		window->Activate();
469}
470
471
472// #pragma mark -
473
474
475int
476main(int, char**)
477{
478	try {
479		new BrowserApp();
480		be_app->Run();
481		delete be_app;
482	} catch (...) {
483		debugger("Exception caught.");
484	}
485
486	return 0;
487}
488
489