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