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