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
BrowserApp()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
~BrowserApp()126 BrowserApp::~BrowserApp()
127 {
128 delete fLaunchRefsMessage;
129 delete fSettings;
130 delete fCookies;
131 delete fSession;
132 }
133
134
135 void
AboutRequested()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
ArgvReceived(int32 argc,char ** argv)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
ReadyToRun()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
MessageReceived(BMessage * message)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
RefsReceived(BMessage * message)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
QuitRequested()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
_RefsReceived(BMessage * message,int32 * _pagesCreated,bool * _fullscreen)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*
_CreateNewPage(const BString & url,BrowserWindow * webWindow,bool fullscreen,bool useBlankTab)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*
_CreateNewWindow(const BString & url,bool fullscreen)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
_CreateNewTab(BrowserWindow * window,const BString & url,bool select)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
_ShowWindow(const BMessage * message,BWindow * window)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
main(int,char **)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