xref: /haiku/src/apps/webpositive/BrowserApp.cpp (revision 040a81419dda83d1014e9dc94936a4cb3f027303)
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 "NetworkCookieJar.h"
51 #include "WebKitInfo.h"
52 #include "WebPage.h"
53 #include "WebSettings.h"
54 #include "WebView.h"
55 
56 
57 #undef B_TRANSLATION_CONTEXT
58 #define B_TRANSLATION_CONTEXT "WebPositive"
59 
60 const char* kApplicationSignature = "application/x-vnd.Haiku-WebPositive";
61 const char* kApplicationName = B_TRANSLATE_SYSTEM_NAME("WebPositive");
62 static const uint32 PRELOAD_BROWSING_HISTORY = 'plbh';
63 
64 #define ENABLE_NATIVE_COOKIES 0
65 
66 
67 BrowserApp::BrowserApp()
68 	:
69 	BApplication(kApplicationSignature),
70 	fWindowCount(0),
71 	fLastWindowFrame(50, 50, 950, 750),
72 	fLaunchRefsMessage(0),
73 	fInitialized(false),
74 	fSettings(NULL),
75 	fCookies(NULL),
76 	fCookieJar(NULL),
77 	fDownloadWindow(NULL),
78 	fSettingsWindow(NULL)
79 {
80 }
81 
82 
83 BrowserApp::~BrowserApp()
84 {
85 	delete fLaunchRefsMessage;
86 	delete fSettings;
87 	delete fCookies;
88 	delete fCookieJar;
89 }
90 
91 
92 void
93 BrowserApp::AboutRequested()
94 {
95 	BAboutWindow* window = new BAboutWindow(kApplicationName,
96 		kApplicationSignature);
97 
98 	// create the about window
99 
100 	const char* authors[] = {
101 		"Andrea Anzani",
102 		"Stephan Aßmus",
103 		"Alexandre Deckner",
104 		"Rene Gollent",
105 		"Ryan Leavengood",
106 		"Michael Lotz",
107 		"Maxime Simon",
108 		NULL
109 	};
110 
111 	BString aboutText("");
112 	aboutText << "HaikuWebKit " << WebKitInfo::HaikuWebKitVersion();
113 	aboutText << " (" << WebKitInfo::HaikuWebKitRevision() << ")";
114 	aboutText << "\nWebKit " << WebKitInfo::WebKitVersion();
115 	aboutText << " (" << WebKitInfo::WebKitRevision() << ")";
116 
117 	window->AddCopyright(2007, "Haiku, Inc.");
118 	window->AddAuthors(authors);
119 	window->AddExtraInfo(aboutText.String());
120 
121 	window->Show();
122 }
123 
124 
125 void
126 BrowserApp::ArgvReceived(int32 argc, char** argv)
127 {
128 	BMessage message(B_REFS_RECEIVED);
129 	for (int i = 1; i < argc; i++) {
130 		if (strcmp("-f", argv[i]) == 0
131 			|| strcmp("--fullscreen", argv[i]) == 0) {
132 			message.AddBool("fullscreen", true);
133 			continue;
134 		}
135 		const char* url = argv[i];
136 		BEntry entry(argv[i], true);
137 		BPath path;
138 		if (entry.Exists() && entry.GetPath(&path) == B_OK)
139 			url = path.Path();
140 		message.AddString("url", url);
141 	}
142 	// Upon program launch, it will buffer a copy of the message, since
143 	// ArgReceived() is called before ReadyToRun().
144 	RefsReceived(&message);
145 }
146 
147 
148 void
149 BrowserApp::ReadyToRun()
150 {
151 	// Since we will essentially run the GUI...
152 	set_thread_priority(Thread(), B_DISPLAY_PRIORITY);
153 
154 	BWebPage::InitializeOnce();
155 	BWebPage::SetCacheModel(B_WEBKIT_CACHE_MODEL_WEB_BROWSER);
156 
157 	BPath path;
158 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK
159 		&& path.Append(kApplicationName) == B_OK
160 		&& create_directory(path.Path(), 0777) == B_OK) {
161 
162 		BWebSettings::SetPersistentStoragePath(path.Path());
163 	}
164 
165 	BString mainSettingsPath(kApplicationName);
166 	mainSettingsPath << "/Application";
167 	fSettings = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
168 		mainSettingsPath.String());
169 #if ENABLE_NATIVE_COOKIES
170 	mainSettingsPath = kApplicationName;
171 	mainSettingsPath << "/Cookies";
172 	fCookies = new SettingsMessage(B_USER_SETTINGS_DIRECTORY,
173 		mainSettingsPath.String());
174 	BMessage cookieArchive;
175 	cookieArchive = fCookies->GetValue("cookies", cookieArchive);
176 	fCookieJar = new BNetworkCookieJar(cookieArchive);
177 	BWebPage::SetCookieJar(fCookieJar);
178 #endif
179 
180 	fLastWindowFrame = fSettings->GetValue("window frame", fLastWindowFrame);
181 	BRect defaultDownloadWindowFrame(-10, -10, 365, 265);
182 	BRect downloadWindowFrame = fSettings->GetValue("downloads window frame",
183 		defaultDownloadWindowFrame);
184 	BRect settingsWindowFrame = fSettings->GetValue("settings window frame",
185 		BRect());
186 	bool showDownloads = fSettings->GetValue("show downloads", false);
187 
188 	fDownloadWindow = new DownloadWindow(downloadWindowFrame, showDownloads,
189 		fSettings);
190 	if (downloadWindowFrame == defaultDownloadWindowFrame) {
191 		// Initially put download window in lower right of screen.
192 		BRect screenFrame = BScreen().Frame();
193 		BMessage decoratorSettings;
194 		fDownloadWindow->GetDecoratorSettings(&decoratorSettings);
195 		float borderWidth = 0;
196 		if (decoratorSettings.FindFloat("border width", &borderWidth) != B_OK)
197 			borderWidth = 5;
198 		fDownloadWindow->MoveTo(screenFrame.Width()
199 			- fDownloadWindow->Frame().Width() - borderWidth,
200 			screenFrame.Height() - fDownloadWindow->Frame().Height()
201 			- borderWidth);
202 	}
203 	fSettingsWindow = new SettingsWindow(settingsWindowFrame, fSettings);
204 
205 	BWebPage::SetDownloadListener(BMessenger(fDownloadWindow));
206 
207 	fInitialized = true;
208 
209 	int32 pagesCreated = 0;
210 	bool fullscreen = false;
211 	if (fLaunchRefsMessage) {
212 		_RefsReceived(fLaunchRefsMessage, &pagesCreated, &fullscreen);
213 		delete fLaunchRefsMessage;
214 		fLaunchRefsMessage = NULL;
215 	}
216 	if (pagesCreated == 0)
217 		_CreateNewWindow("", fullscreen);
218 
219 	PostMessage(PRELOAD_BROWSING_HISTORY);
220 }
221 
222 
223 void
224 BrowserApp::MessageReceived(BMessage* message)
225 {
226 	switch (message->what) {
227 	case PRELOAD_BROWSING_HISTORY:
228 		// Accessing the default instance will load the history from disk.
229 		BrowsingHistory::DefaultInstance();
230 		break;
231 	case B_SILENT_RELAUNCH:
232 		_CreateNewPage("");
233 		break;
234 	case NEW_WINDOW: {
235 		BString url;
236 		if (message->FindString("url", &url) != B_OK)
237 			break;
238 		_CreateNewWindow(url);
239 		break;
240 	}
241 	case NEW_TAB: {
242 		BrowserWindow* window;
243 		if (message->FindPointer("window",
244 			reinterpret_cast<void**>(&window)) != B_OK)
245 			break;
246 		BString url;
247 		message->FindString("url", &url);
248 		bool select = false;
249 		message->FindBool("select", &select);
250 		_CreateNewTab(window, url, select);
251 		break;
252 	}
253 	case WINDOW_OPENED:
254 		fWindowCount++;
255 		fDownloadWindow->SetMinimizeOnClose(false);
256 		break;
257 	case WINDOW_CLOSED:
258 		fWindowCount--;
259 		message->FindRect("window frame", &fLastWindowFrame);
260 		if (fWindowCount <= 0)
261 			PostMessage(B_QUIT_REQUESTED);
262 		break;
263 
264 	case SHOW_DOWNLOAD_WINDOW:
265 		_ShowWindow(message, fDownloadWindow);
266 		break;
267 	case SHOW_SETTINGS_WINDOW:
268 		_ShowWindow(message, fSettingsWindow);
269 		break;
270 
271 	default:
272 		BApplication::MessageReceived(message);
273 		break;
274 	}
275 }
276 
277 
278 void
279 BrowserApp::RefsReceived(BMessage* message)
280 {
281 	if (!fInitialized) {
282 		delete fLaunchRefsMessage;
283 		fLaunchRefsMessage = new BMessage(*message);
284 		return;
285 	}
286 
287 	_RefsReceived(message);
288 }
289 
290 
291 bool
292 BrowserApp::QuitRequested()
293 {
294 	if (fDownloadWindow->DownloadsInProgress()) {
295 		BAlert* alert = new BAlert(B_TRANSLATE("Downloads in progress"),
296 			B_TRANSLATE("There are still downloads in progress, do you really "
297 			"want to quit WebPositive now?"), B_TRANSLATE("Quit"),
298 			B_TRANSLATE("Continue downloads"));
299 		int32 choice = alert->Go();
300 		if (choice == 1) {
301 			if (fWindowCount == 0) {
302 				if (fDownloadWindow->Lock()) {
303 					fDownloadWindow->SetWorkspaces(1 << current_workspace());
304 					if (fDownloadWindow->IsHidden())
305 						fDownloadWindow->Show();
306 					else
307 						fDownloadWindow->Activate();
308 					fDownloadWindow->SetMinimizeOnClose(true);
309 					fDownloadWindow->Unlock();
310 					return false;
311 				}
312 			} else
313 				return false;
314 		}
315 	}
316 
317 	for (int i = 0; BWindow* window = WindowAt(i); i++) {
318 		BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
319 		if (!webWindow)
320 			continue;
321 		if (!webWindow->Lock())
322 			continue;
323 		if (webWindow->QuitRequested()) {
324 			fLastWindowFrame = webWindow->WindowFrame();
325 			webWindow->Quit();
326 			i--;
327 		} else {
328 			webWindow->Unlock();
329 			return false;
330 		}
331 	}
332 
333 	BWebPage::ShutdownOnce();
334 
335 	fSettings->SetValue("window frame", fLastWindowFrame);
336 	if (fDownloadWindow->Lock()) {
337 		fSettings->SetValue("downloads window frame", fDownloadWindow->Frame());
338 		fSettings->SetValue("show downloads", !fDownloadWindow->IsHidden());
339 		fDownloadWindow->Unlock();
340 	}
341 	if (fSettingsWindow->Lock()) {
342 		fSettings->SetValue("settings window frame", fSettingsWindow->Frame());
343 		fSettingsWindow->Unlock();
344 	}
345 
346 	BMessage cookieArchive;
347 	if (fCookieJar != NULL && fCookieJar->Archive(&cookieArchive) == B_OK)
348 		fCookies->SetValue("cookies", cookieArchive);
349 
350 	return true;
351 }
352 
353 
354 void
355 BrowserApp::_RefsReceived(BMessage* message, int32* _pagesCreated,
356 	bool* _fullscreen)
357 {
358 	int32 pagesCreated = 0;
359 
360 	bool fullscreen;
361 	if (message->FindBool("fullscreen", &fullscreen) != B_OK)
362 		fullscreen = false;
363 
364 	entry_ref ref;
365 	for (int32 i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
366 		BEntry entry(&ref, true);
367 		if (!entry.Exists())
368 			continue;
369 		BPath path;
370 		if (entry.GetPath(&path) != B_OK)
371 			continue;
372 		BString url;
373 		url << path.Path();
374 		_CreateNewPage(url, fullscreen);
375 		pagesCreated++;
376 	}
377 
378 	BString url;
379 	for (int32 i = 0; message->FindString("url", i, &url) == B_OK; i++) {
380 		_CreateNewPage(url, fullscreen);
381 		pagesCreated++;
382 	}
383 
384 	if (_pagesCreated != NULL)
385 		*_pagesCreated = pagesCreated;
386 	if (_fullscreen != NULL)
387 		*_fullscreen = fullscreen;
388 }
389 
390 
391 void
392 BrowserApp::_CreateNewPage(const BString& url, bool fullscreen)
393 {
394 	uint32 workspace = 1 << current_workspace();
395 
396 	bool loadedInWindowOnCurrentWorkspace = false;
397 	for (int i = 0; BWindow* window = WindowAt(i); i++) {
398 		BrowserWindow* webWindow = dynamic_cast<BrowserWindow*>(window);
399 		if (!webWindow)
400 			continue;
401 		if (webWindow->Lock()) {
402 			if (webWindow->Workspaces() & workspace) {
403 				if (webWindow->IsBlankTab()) {
404 					if (url.Length() != 0)
405 						webWindow->CurrentWebView()->LoadURL(url);
406 				} else
407 					webWindow->CreateNewTab(url, true);
408 				webWindow->Activate();
409 				webWindow->CurrentWebView()->MakeFocus(true);
410 				loadedInWindowOnCurrentWorkspace = true;
411 			}
412 			webWindow->Unlock();
413 		}
414 		if (loadedInWindowOnCurrentWorkspace)
415 			return;
416 	}
417 	_CreateNewWindow(url, fullscreen);
418 }
419 
420 
421 void
422 BrowserApp::_CreateNewWindow(const BString& url, bool fullscreen)
423 {
424 	// Offset the window frame unless this is the first window created in the
425 	// session.
426 	if (fWindowCount > 0)
427 		fLastWindowFrame.OffsetBy(20, 20);
428 	if (!BScreen().Frame().Contains(fLastWindowFrame))
429 		fLastWindowFrame.OffsetTo(50, 50);
430 
431 	BrowserWindow* window = new BrowserWindow(fLastWindowFrame, fSettings,
432 		url);
433 	if (fullscreen)
434 		window->ToggleFullscreen();
435 	window->Show();
436 }
437 
438 
439 void
440 BrowserApp::_CreateNewTab(BrowserWindow* window, const BString& url,
441 	bool select)
442 {
443 	if (!window->Lock())
444 		return;
445 	window->CreateNewTab(url, select);
446 	window->Unlock();
447 }
448 
449 
450 void
451 BrowserApp::_ShowWindow(const BMessage* message, BWindow* window)
452 {
453 	BAutolock _(window);
454 	uint32 workspaces;
455 	if (message->FindUInt32("workspaces", &workspaces) == B_OK)
456 		window->SetWorkspaces(workspaces);
457 	if (window->IsHidden())
458 		window->Show();
459 	else
460 		window->Activate();
461 }
462 
463 
464 // #pragma mark -
465 
466 
467 int
468 main(int, char**)
469 {
470 	try {
471 		new BrowserApp();
472 		be_app->Run();
473 		delete be_app;
474 	} catch (...) {
475 		debugger("Exception caught.");
476 	}
477 
478 	return 0;
479 }
480 
481