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