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