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