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