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