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